MetPy is a modern meteorological open-source toolkit for Python. It is a maintained project of Unidata to serve the academic meteorological community. MetPy consists of three major areas of functionality:
As meteorologists, we have many field specific plots that we make. Some of these, such as the Skew-T Log-p require non-standard axes and are difficult to plot in most plotting software. In MetPy we've baked in a lot of this specialized functionality to help you get your plots made and get back to doing science. We will go over making different kinds of plots during the workshop.
Meteorology also has a common set of calculations that everyone ends up programming themselves. This is error-prone and a huge duplication of work! MetPy contains a set of well tested calculations that is continually growing in an effort to be at feature parity with other legacy packages such as GEMPAK.
Finally, there are a number of odd file formats in the meteorological community. MetPy has incorporated a set of readers to help you deal with file formats that you may encounter during your research.
In order for us to discuss any of the functionality of MetPy, we first need to understand how units are inherently a part of MetPy and how to use them within this library.
Early in our scientific careers we all learn about the importance of paying attention to units in our calculations. Unit conversions can still get the best of us and have caused more than one major technical disaster, including the crash and complete loss of the $327 million Mars Climate Orbiter.
In MetPy, we use the pint library and a custom unit registry to help prevent unit mistakes in calculations. That means that every quantity you pass to MetPy should have units attached, just like if you were doing the calculation on paper! Attaching units is easy:
In [ ]:
# Import the MetPy unit registry
from metpy.units import units
In [ ]:
length = 10.4 * units.inches
width = 20 * units.meters
print(length, width)
Don't forget that you can use tab completion to see what units are available! Just about every imaginable quantity is there, but if you find one that isn't, we're happy to talk about adding it.
While it may seem like a lot of trouble, let's compute the area of a rectangle defined by our length and width variables above. Without units attached, you'd need to remember to perform a unit conversion before multiplying or you would end up with an area in inch-meters and likely forget about it. With units attached, the units are tracked for you.
In [ ]:
area = length * width
print(area)
That's great, now we have an area, but it is not in a very useful unit still. Units can be converted using the .to()
method. While you won't see m$^2$ in the units list, we can parse complex/compound units as strings:
In [ ]:
area.to('m^2')
In [ ]:
# Your code goes here
In [ ]:
# %load solutions/distance.py
Temperature units are actually relatively tricky (more like absolutely tricky as you'll see). Temperature is a non-multiplicative unit - they are in a system with a reference point. That means that not only is there a scaling factor, but also an offset. This makes the math and unit book-keeping a little more complex. Imagine adding 10 degrees Celsius to 100 degrees Celsius. Is the answer 110 degrees Celsius or 383.15 degrees Celsius (283.15 K + 373.15 K)? That's why there are delta degrees units in the unit registry for offset units. For more examples and explanation you can watch MetPy Monday #13.
Let's take a look at how this works and fails:
We would expect this to fail because we cannot add two offset units (and it does fail as an "Ambiguous operation with offset unit").
10 * units.degC + 5 * units.degC
On the other hand, we can subtract two offset quantities and get a delta:
In [ ]:
10 * units.degC - 5 * units.degC
We can add a delta to an offset unit as well:
In [ ]:
25 * units.degC + 5 * units.delta_degF
Absolute temperature scales like Kelvin and Rankine do not have an offset and therefore can be used in addition/subtraction without the need for a delta verion of the unit.
In [ ]:
273 * units.kelvin + 10 * units.kelvin
In [ ]:
273 * units.kelvin - 10 * units.kelvin
In [ ]:
# 12 UTC temperature
temp_initial = 20 * units.degC
temp_initial
Maybe the surface temperature increased by 5 degrees Celsius so far today - is this a temperature of 5 degC, or a temperature change of 5 degC? We subconsciously know that its a delta of 5 degC, but often write it as just adding two temperatures together, when it really is: temperature + delta(temperature)
In [ ]:
# New 18 UTC temperature
temp_new = temp_initial + 5 * units.delta_degC
temp_new
In [ ]:
# Your code goes here
In [ ]:
# %load solutions/temperature_change.py
Another common place that problems creep into scientific code is the value of constants. Can you reproduce someone else's computations from their paper? Probably not unless you know the value of all of their constants. Was the radius of the earth 6000 km, 6300km, 6371 km, or was it actually latitude dependent?
MetPy has a set of constants that can be easily accessed and make your calculations reproducible. You can view a full table in the docs, look at the module docstring with metpy.constants?
or checkout what's available with tab completion.
In [ ]:
import metpy.constants as mpconst
In [ ]:
mpconst.earth_avg_radius
In [ ]:
mpconst.dry_air_molecular_weight
You may also notice in the table that most constants have a short name as well that can be used:
In [ ]:
mpconst.Re
In [ ]:
mpconst.Md
MetPy also encompasses a set of calculations that are common in meteorology (with the goal of have all of the functionality of legacy software like GEMPAK and more). The calculations documentation has a complete list of the calculations in MetPy.
We'll scratch the surface and show off a few simple calculations here, but will be using many during the workshop.
In [ ]:
import metpy.calc as mpcalc
import numpy as np
In [ ]:
# Make some fake data for us to work with
np.random.seed(19990503) # So we all have the same data
u = np.random.randint(0, 15, 10) * units('m/s')
v = np.random.randint(0, 15, 10) * units('m/s')
print(u)
print(v)
Let's use the wind_direction
function from MetPy to calculate wind direction from these values. Remember you can look at the docstring or the website for help.
In [ ]:
direction = mpcalc.wind_direction(u, v)
print(direction)
In [ ]:
# Your code goes here
In [ ]:
# %load solutions/wind_speed.py
As one final demonstration, we will calculation the dewpoint given the temperature and relative humidity:
In [ ]:
mpcalc.dewpoint_rh(25 * units.degC, 75 * units.percent)